﻿using System.ComponentModel;
using System.Diagnostics;
using System.Text;

namespace Commander
{
    /// <summary>
    /// 运行器
    /// </summary>
    public class Runner : IDisposable
    {
        // 定义事件
        public delegate void CommandOutputLineEventHandler(object sender, CommandOutputLineEventArgs e);
        public event CommandOutputLineEventHandler OutputLine;
        public event CommandOutputLineEventHandler ErrorLine;

        // 私有变量
        private readonly ProcessStartInfo _processStartInfo;
        private StreamWriter? _input = null;
        private StreamReader? _output = null;
        private StreamReader? _error = null;

        /// <summary>
        /// 输出内容
        /// </summary>
        public List<string> Ouputs { get; private set; }

        /// <summary>
        /// 错误内容
        /// </summary>
        public List<string> Errors { get; private set; }

        /// <summary>
        /// 是否运行中
        /// </summary>
        public bool IsRunning { get; private set; }

        /// <summary>
        /// 命令字符串
        /// </summary>
        public string Command { get; private set; } = "";

        // 初始化
        private void Init()
        {
            this.IsRunning = false;
            _processStartInfo.UseShellExecute = false;
            _processStartInfo.RedirectStandardOutput = true;
            _processStartInfo.RedirectStandardError = true;
            _processStartInfo.RedirectStandardInput = true;
            _processStartInfo.StandardInputEncoding = Encoding.UTF8;
            _processStartInfo.StandardOutputEncoding = Encoding.UTF8;
            _processStartInfo.StandardErrorEncoding = Encoding.UTF8;
            _processStartInfo.CreateNoWindow = true;
            if (_processStartInfo.Arguments == "")
            {
                this.Command = _processStartInfo.FileName;
            }
            else
            {
                this.Command = _processStartInfo.FileName + " " + _processStartInfo.Arguments;
            }
        }

        /// <summary>
        /// 运行器
        /// </summary>
        public Runner(ProcessStartInfo processStartInfo)
        {
            this.OutputLine += new CommandOutputLineEventHandler((sender, e) => { });
            this.ErrorLine += new CommandOutputLineEventHandler((sender, e) => { });
            this.Ouputs = new List<string>();
            this.Errors = new List<string>();
            _processStartInfo = processStartInfo;
            this.Init();
        }

        /// <summary>
        /// 运行器
        /// </summary>
        /// <param name="path">程序路径</param>
        /// <param name="arg">运行参数</param>
        /// <param name="workPath">工作目录</param>
        public Runner(string path, string? arg = null, string? workPath = null)
        {
            this.OutputLine += new CommandOutputLineEventHandler((sender, e) => { });
            this.ErrorLine += new CommandOutputLineEventHandler((sender, e) => { });
            this.Ouputs = new List<string>();
            this.Errors = new List<string>();
            _processStartInfo = new ProcessStartInfo(path);
            if (arg != null) _processStartInfo.Arguments = arg;
            if (workPath != null) _processStartInfo.WorkingDirectory = workPath;
            this.Init();
        }

        /// <summary>
        /// 执行程序
        /// </summary>
        /// <returns></returns>
        public string Run()
        {
            //List<byte> bs = new List<byte>();
            StringBuilder sb = new StringBuilder();
            var process = Process.Start(_processStartInfo);
            if (process is null) throw new CommandException($"程序'{_processStartInfo.FileName}'执行失败");
            this.IsRunning = true;
            this.Ouputs.Clear();
            this.Errors.Clear();
            process.Exited += Process_Exited;
            _input = process.StandardInput;
            _output = process.StandardOutput;
            _error = process.StandardError;
            Task.Run(() =>
            {
                while (!process.HasExited)
                {
                    string? line = _output.ReadLine();
                    if (line != null)
                    {
                        sb.AppendLine(line);
                        this.Ouputs.Add(line);
                        // 触发事件
                        this.OutputLine(this, new CommandOutputLineEventArgs() { Content = line });
                    }
                }
            });
            Task.Run(() =>
            {
                while (!process.HasExited)
                {
                    string? line = _error.ReadLine();
                    if (line != null)
                    {
                        sb.AppendLine(line);
                        this.Errors.Add(line);
                        // 触发事件
                        this.ErrorLine(this, new CommandOutputLineEventArgs() { Content = line });
                    }
                }
            });
            process.WaitForExit();
            return sb.ToString();
        }

        // 线程退出
        private void Process_Exited(object? sender, EventArgs e)
        {
            this.IsRunning = false;
        }

        public void Dispose()
        {
            //throw new NotImplementedException();
            _input?.Dispose();
            _output?.Dispose();
            _error?.Dispose();
            GC.SuppressFinalize(this);
        }
    }
}